#include "DB.h"


/**************************************************/
// private functions

// low level byte write
void SD_DB::DBWrite(uint32_t ee, const byte* p, uint16_t recsize)
{
  for (uint16_t i = 0; i < recsize; i++)
    DB_write_byte(ee++, *p++);
}

// low level byte read
void SD_DB::DBRead(uint32_t ee, byte* p, uint16_t recsize)
{  
  for (unsigned i = 0; i < recsize; i++)
    *p++ = DB_read_byte(ee++);
}

// writes DB_Header
void SD_DB::DBwriteHead()
{
  DBWrite(DB_head_ptr, DB_REC DB_head, (uint16_t)sizeof(DB_Header));
}

// reads DB_Header
void SD_DB::DBreadHead()
{
  DBRead(DB_head_ptr, DB_REC DB_head, (uint16_t)sizeof(DB_Header));
}

/**************************************************/
// public functions

SD_DB::SD_DB()
{

}


void SD_DB::DB_write_byte(uint32_t address, byte data)
{

}

byte SD_DB::DB_read_byte(uint32_t address)
{
  return 0;
}

// creates a new table and sets header values 
DB_Status SD_DB::DBcreate(uint32_t head_ptr, uint32_t tablesize, uint16_t recsize)
{
  DB_head_ptr = head_ptr;
  DB_table_ptr = sizeof(DB_Header) + DB_head_ptr;
  DB_head.n_recs = 0;
  DB_head.rec_size = recsize;
  DB_head.table_size = tablesize;
  DBwriteHead();
  return DB_OK;
}

// reads an existing edb header at a given recno and sets header values
DB_Status SD_DB::DBopen(uint32_t head_ptr)
{
  DB_head_ptr = head_ptr;
  DBreadHead();
  DB_table_ptr = sizeof(DB_Header) + DB_head_ptr;
/*
Serial.println(DB_head.n_recs);
Serial.println(DB_head.rec_size);
Serial.println(DB_head.table_size);
*/
  return DB_OK;
}

// writes a record to a given recno
DB_Status SD_DB::DBwriteRec(uint32_t recno, const DB_Rec rec)
{

  DBWrite(DB_table_ptr + ((recno - 1) * DB_head.rec_size), rec, DB_head.rec_size);
  return DB_OK;
}

// reads a record from a given recno
DB_Status SD_DB::DBreadRec(uint32_t recno, DB_Rec rec)
{
  if (recno < 1 || recno > DB_head.n_recs) return DB_OUT_OF_RANGE;
  DBRead(DB_table_ptr + ((recno - 1) * DB_head.rec_size), rec, DB_head.rec_size);
  return DB_OK;
}

// Deletes a record at a given recno
// Becomes more inefficient as you the record set increases and you delete records 
// early in the record queue.
DB_Status SD_DB::DBdeleteRec(uint32_t recno)
{
  if (recno < 0 || recno > DB_head.n_recs) return  DB_OUT_OF_RANGE;
  DB_Rec rec = (byte*)malloc(DB_head.rec_size);
  for (uint32_t i = recno + 1; i <= DB_head.n_recs; i++)
  {
    DBreadRec(i, rec);
    DBwriteRec(i - 1, rec);
  }  
  free(rec);
  DB_head.n_recs--;
  DBwriteHead();
  return DB_OK;
}

// Inserts a record at a given recno, increasing all following records' recno by 1.
// This function becomes increasingly inefficient as it's currently implemented and 
// is the slowest way to add a record.
DB_Status SD_DB::DBinsertRec(uint32_t recno, DB_Rec rec)
{
  if (DBcount() == DBlimit()) return DB_TABLE_FULL;
  if (DBcount() > 0 && (recno < 0 || recno > DB_head.n_recs)) return DB_OUT_OF_RANGE;
  if (DBcount() == 0 && recno == 1) return DBappendRec(rec);

  DB_Rec buf = (byte*)malloc(DB_head.rec_size);
  for (uint32_t i = DB_head.n_recs; i >= recno; i--)
  {
    DBreadRec(i, buf);
    DBwriteRec(i + 1, buf);
  }
  free(buf);
  DBwriteRec(recno, rec);  
  DB_head.n_recs++;
  DBwriteHead();
  return DB_OK;
}

// Updates a record at a given recno
DB_Status SD_DB::DBupdateRec(uint32_t recno, DB_Rec rec)
{
  if (recno < 0 || recno > DB_head.n_recs) return DB_OUT_OF_RANGE;
  DBwriteRec(recno, rec);  
  return DB_OK;
}

// Adds a record to the end of the record set.
// This is the fastest way to add a record.
DB_Status SD_DB::DBappendRec(DB_Rec rec)
{
  if (DB_head.n_recs + 1 > DBlimit()) return DB_TABLE_FULL;
  DB_head.n_recs++;
  DBwriteRec(DB_head.n_recs,rec);
  DBwriteHead();
  return DB_OK;
}

// returns the number of queued items
uint32_t SD_DB::DBcount()
{
  return DB_head.n_recs;
}

// returns the maximum number of items that will fit into the queue
uint32_t SD_DB::DBlimit()
{
   return (DB_head.table_size - DB_table_ptr) / DB_head.rec_size;
}

// truncates the queue by resetting the internal pointers
void SD_DB::DBclear()
{
  DBreadHead();
  DBcreate(DB_head_ptr, DB_head.table_size, DB_head.rec_size);
}


